/*
* 语音听写(iFly Auto Transform)技术能够实时地将语音转换成对应的文字。
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include "speech_recognizer.h"

#include "aikit_biz_api.h"
#include "aikit_constant.h"
#include "aikit_biz_config.h"
#include "simple_kws.h"

using namespace std;
using namespace AIKIT;
static const char *ABILITY = "e867a88f2";

// KWS相关全局变量
static int g_kws_enabled = 0;

// 信号处理函数
void signal_handler(int signal) {
    printf("\n接收到信号 %d，正在停止KWS...\n", signal);
    g_kws_enabled = 0;
    simple_kws_stop();
}

void OnOutput(AIKIT_HANDLE* handle, const AIKIT_OutputData* output){
    printf("OnOutput abilityID :%s\n",handle->abilityID);
    printf("OnOutput key:%s\n",output->node->key);
    printf("OnOutput value:%s\n",(char*)output->node->value);

    if(output->node != nullptr && output->node->value != nullptr)
    {
        FILE *fin = fopen("ivw_result.txt", "ab");
        if (fin == nullptr)
        {
            printf("文件打开失败！");
            return;
        }
        fwrite(output->node->value, sizeof(char), output->node->len, fin);
        fwrite("\n", sizeof(char), strlen("\n"), fin);
        fclose(fin);
    }    
}
void OnEvent(AIKIT_HANDLE* handle, AIKIT_EVENT eventType, const AIKIT_OutputEvent* eventValue){
    printf("OnEvent:%d\n",eventType);
}

void OnError(AIKIT_HANDLE* handle, int32_t err, const char* desc){
    printf("OnError:%d\n",err);
}

void ivwIns(const char* audio_path,int keywordfiles_count){
    AIKIT_ParamBuilder* paramBuilder = nullptr;
    AIKIT_DataBuilder* dataBuilder = nullptr;
    AIKIT_HANDLE* handle = nullptr;
    AiAudio* aiAudio_raw = nullptr;
    int fileSize = 0;
    int readLen = 0;
    FILE * file = nullptr;
    char data[320] = {0};
    int *index = NULL;
    int writetimes = 0;
    int ret = 0;

    paramBuilder = AIKIT_ParamBuilder::create();
    index = (int *)malloc(keywordfiles_count * sizeof(int));
    for (int i = 0; i < keywordfiles_count;++i)
        index[i] = i;
    ret = AIKIT_SpecifyDataSet(ABILITY, "key_word", index, keywordfiles_count);
    printf("AIKIT_SpecifyDataSet:%d\n", ret);
    if(ret != 0){
        goto  exit;
    }
    paramBuilder->param("wdec_param_nCmThreshold","0 0:1000",strlen("0 0:1000"));
    paramBuilder->param("gramLoad",true);
    ret = AIKIT_Start(ABILITY,AIKIT_Builder::build(paramBuilder),nullptr,&handle);
    printf("AIKIT_Start:%d\n",ret);
    if(ret != 0){
        goto  exit;
    }

    file = fopen(audio_path,"rb");
    if(file == nullptr) {
        printf("fopen failed\n");
        goto exit;
    }
    fseek(file,0,SEEK_END);
    fileSize = ftell(file);
    fseek(file,0,SEEK_SET);

    dataBuilder = AIKIT_DataBuilder::create();
    while (fileSize > 0) {
        readLen = fread(data,1,sizeof(data),file);
        dataBuilder->clear();

        aiAudio_raw = AiAudio::get("wav")->data(data,320)->valid();
        dataBuilder->payload(aiAudio_raw);
        ret = AIKIT_Write(handle,AIKIT_Builder::build(dataBuilder));
        writetimes++;
        if(ret != 0){
            printf("AIKIT_Write:%d\n",ret);
            goto  exit;
        }
       fileSize -= readLen;
    }
    ret = AIKIT_End(handle);

 exit:   
    if(index != NULL)
        free(index);
    if(file != nullptr){
        fclose(file);
        file = nullptr;
    }
    if(handle != nullptr)
        AIKIT_End(handle);

    if(paramBuilder != nullptr){
        delete paramBuilder;
        paramBuilder = nullptr;
    }

    if(dataBuilder != nullptr){
        delete dataBuilder;
        dataBuilder = nullptr;
    }
}

/* demo recognize the audio from microphone */
static void demo_mic(int keywordfiles_count)
{
    printf("record start!\n");
	int errcode;

	struct speech_rec ivw;

	errcode = sr_init(&ivw, keywordfiles_count,SR_MIC);
	if (errcode) {
		printf("speech recognizer init failed\n");
		return;
	}
	errcode = sr_start_listening(&ivw);
	if (errcode) {
		printf("start listen failed %d\n", errcode);
	}
	char end_command;
    system("stty -icanon");
    while(1)
    {
        end_command = getchar();
        if(end_command == 's')
            break;
        sleep(1);
    }
	errcode = sr_stop_listening(&ivw);
	if (errcode) {
		printf("stop listening failed %d\n", errcode);
	}

	sr_uninit(&ivw);
}

void TestIvw70()
{
    int count = 1;
    int aud_src = 0;

    int ret = AIKIT_Init();
    if(ret != 0){
        printf("AIKIT_Init failed:%d\n",ret);
        goto exit;
    }

    ret = AIKIT_EngineInit(ABILITY,nullptr);
    if(ret != 0){
        printf("AIKIT_EngineInit failed:%d\n",ret);
        goto exit;
    }

    AIKIT_CustomData customData;
    customData.key = "key_word";
    customData.index = 0;
    customData.from = AIKIT_DATA_PTR_PATH;
    customData.value = (void *)"/demo/resource/many-keywords.txt";
    customData.len = strlen("/demo/resource/many-keywords.txt");
    customData.next = nullptr;
    customData.reserved = nullptr;
    printf("AIKIT_LoadData start!\n");
    ret = AIKIT_LoadData(ABILITY, &customData);
    printf("AIKIT_LoadData end!\n");
    printf("AIKIT_LoadData:%d\n", ret);
    if (ret != 0)
    {
        goto exit;
    }

    printf("Where the audio comes from?\n"
			"0: From a audio file.\n1: From microphone.\n");
	scanf("%d", &aud_src);
	if(aud_src != 0) {
		printf("Demo recognizing the speech from microphone\n");
        printf("\n\
=================================\n\
    press r to start recording\n\
    press s to end recording\n\
==================================\n");

        char start_command;
        scanf("%s",&start_command);
        if(start_command == 'r')
            demo_mic(count);
        else
        {
            printf("please press r to start recording, if not, it will quit!\n");
            scanf("%s",&start_command);
            if(start_command == 'r')
                demo_mic(count);
        }

		printf("record end\n");
	} else {
        //批量读取文件
        FILE *wav_scp = NULL;
        char wav_file[100];

        wav_scp = fopen("wav.scp", "r");
        if (wav_scp == NULL)
            goto exit;

        while (fgets(wav_file, 100, wav_scp))
        {
            wav_file[strcspn(wav_file, "\n")] = '\0';
            FILE *fin = fopen("ivw_result.txt", "ab");
            if (fin == nullptr)
            {
                printf("文件打开失败！");
                return;
            }
            fwrite(wav_file, sizeof(char), strlen(wav_file), fin);
            fwrite("\n", sizeof(char), strlen("\n"), fin);
            fclose(fin);
            ivwIns(wav_file, count);
            sleep(0.5);
        }

        if (wav_scp != NULL)
        {
            fclose(wav_scp);
            wav_scp = NULL;
        }

        //读取单个音频
        //ivwIns("./audioWav/F_F001_096_21C1-3_out_out.wav", 1);
    }
exit:
    AIKIT_UnLoadData(ABILITY,"key_word",0);

    AIKIT_EngineUnInit(ABILITY);
    AIKIT_UnInit();
}

// 测试简化KWS接口
void TestKWSInterface(const char* resource_path, int keyword_count) {
    printf("\n=== 测试简化KWS接口 ===\n");
    printf("资源路径: %s\n", resource_path);
    printf("关键词数量: %d\n", keyword_count);
    printf("==================\n");

    // Local callbacks for TestKWSInterface
    auto test_on_keyword_detected = [](const char* keyword) {
        printf("\n=== TestKWSInterface检测到关键词 ===\n");
        printf("关键词: \"%s\"\n", keyword);
        printf("========================\n");
    };

    auto test_on_error = [](const char* errorMsg) {
        fprintf(stderr, ">>> TestKWSInterface错误: %s\n", errorMsg);
    };

    // 初始化KWS
    printf("初始化KWS...\n");
    if (simple_kws_init(resource_path, keyword_count, test_on_keyword_detected, test_on_error) != 0) {
        fprintf(stderr, "KWS初始化失败\n");
        return;
    }
    printf("KWS初始化成功\n");

    // 启动关键词识别
    printf("启动关键词识别...\n");
    if (simple_kws_start() != 0) {
        fprintf(stderr, "启动KWS失败\n");
        simple_kws_cleanup();
        return;
    }
    printf("正在监听关键词... (按Ctrl+C停止)\n");

    g_kws_enabled = 1;

    // 主循环
    while (g_kws_enabled) {
        sleep(1);
    }

    // 清理资源
    printf("清理KWS资源...\n");
    simple_kws_cleanup();
    printf("KWS测试完成\n");
}

/* main thread: start/stop record ; query the result of recgonization.
 * record thread: record callback(data write)
 * helper thread: ui(keystroke detection)
 */
int main(int argc, char* argv[])
{
    printf("=== IVW + KWS 语音识别系统 ===\n");
    printf("支持的模式:\n");
    printf("  1. 原始IVW识别（默认）\n");
    printf("  2. 新KWS接口测试（--kws参数）\n");
    printf("============================\n");

    // 检查是否使用KWS接口
    bool use_kws_interface = false;
    const char* kws_resource_path = "/demo/resource/many-keywords.txt";
    int kws_keyword_count = 1;

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--kws") == 0) {
            use_kws_interface = true;
            printf("使用KWS接口模式\n");
        } else if (strcmp(argv[i], "--kws-resource") == 0 && i + 1 < argc) {
            kws_resource_path = argv[++i];
            printf("KWS资源路径: %s\n", kws_resource_path);
        } else if (strcmp(argv[i], "--kws-count") == 0 && i + 1 < argc) {
            kws_keyword_count = atoi(argv[++i]);
            printf("KWS关键词数量: %d\n", kws_keyword_count);
        }
    }

    if (use_kws_interface) {
        // 使用新的KWS C接口
        printf("\n进入KWS接口模式...\n");

        // 注册信号处理函数
        signal(SIGINT, signal_handler);
        signal(SIGTERM, signal_handler);

        TestKWSInterface(kws_resource_path, kws_keyword_count);
    } else {
        // 使用原始IVW实现
        printf("\n进入原始IVW模式...\n");

        AIKIT_Configurator::builder()
            .app()
                .appID("bcf15145")
                .apiSecret("NGMzYmI5MTE0MGYzYjk1ODRlMWRhNDA1")
                .apiKey("3a0e16e1a988d976d094b3835dd899e3")
                .workDir("./")
                .resDir("/oem/aikit/resource/")
                .cfgFile("")
            .auth()
                .authType(0)
                .ability(ABILITY)
            .log()
                .logLevel(LOG_LVL_VERBOSE)
                .logMode(LOG_FILE)
                .logPath("../log/ivw_aikit.log");

        AIKIT_Callbacks cbs = {OnOutput,OnEvent,OnError};
        AIKIT_RegisterAbilityCallback(ABILITY,cbs);

        TestIvw70();
    }

    return 0;
}
